package cn.rengy.isql;

import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

import java.util.*;

/**
 * <p>NamedParameterUtils 基础上修改
 * <p>支持?参数解析成多个： in(?) ==> in(?,?,?),当SqlParameterSource是数组或集合时解析为多个?
 * <p>支持具名参数，语法：name=:name ==> name=?,?,?(1个或多个)
 * <p>支持可变参数，语法：${name=:name} name不为空才会解析==> "" 或者 name=?,?,?(1个或多个)
 * @author rengy
 *
 */
public  class SqlParser {

	/**
	 * 注释或引号起始字符的字符集。
	 */
	private static final String[] START_SKIP = new String[] {"'", "\"", "--", "/*"};

	/**
	 * 和注释或引号起始字符对应的结尾字符的字符集。
	 */
	private static final String[] STOP_SKIP = new String[] {"'", "\"", "\n", "*/"};

	/**
	 *  符合参数分隔符条件的字符集，空格也是分隔符，不在这里定义
		指示SQL字符串中的参数名称已结束。
	 */
	private static final String PARAMETER_SEPARATORS = "\"':&,;()|=+-*%/\\<>^";

	/**
	 *  每个字符代码带有分隔符标志的索引。
		范围在ASCII码从34到124之间。
	 */
	private static final boolean[] separatorIndex = new boolean[128];

	static {
		for (char c : PARAMETER_SEPARATORS.toCharArray()) {
			separatorIndex[c] = true;
		}
	}
	/**
	 * 解析sql
	 * @param sql sql模板
	 */
	public static VariableParsedSql parseSql(final String sql) {
		Assert.notNull(sql, "SQL must not be null");

		StringBuilder originalSql = new StringBuilder();//原始SQL 包含注释， 不包含可变参数
		StringBuilder actualSql = new StringBuilder();//执行SQL 不包含可变参数，named参数只转换成一个?
		int unnamedParameterCount=0;
		List<ParameterHolder> parameterList = new ArrayList<>();
		char[] statement = sql.toCharArray();
		int i = 0;
		int lastAppendIndex=0;
		while (i < statement.length) {
			int skipToPosition = i;
			while (i < statement.length) {
				//跳过注释和引用
				skipToPosition = skipCommentsAndQuotes(statement, i);
				if (i == skipToPosition) {
					break;
				} else {
					i = skipToPosition;
				}
			}
			if (i >= statement.length) {
				break;
			}
			char c = statement[i];
			if(c=='$') {
				int j = i + 1;
				if(j < statement.length&&statement[j] == '{') {
					j ++;
					//不支持再匹配${}
					while (j < statement.length && statement[j] != '}'){
						if(statement[j] == '{') {
							throw new IllegalArgumentException("${}不支持嵌套") ;
						}
						j++;
					}
//					int matchingIndex=0;
//					while (j < statement.length && (statement[j] != '}'||matchingIndex!=0)){
//						if(statement[j] == '{') {
//							matchingIndex++;
//						}
//						if(statement[j] == '}') {
//							matchingIndex--;
//						}
//						j++;
//					}
					String variableSql = sql.substring(i + 2, j);
					originalSql.append(sql,lastAppendIndex,i);
					actualSql.append(sql,lastAppendIndex,i);
					parameterList.add(new VariableParameterHolder(originalSql.length(),parseSql(variableSql)));
					//sqlToUse.delete(i- escapes, j- escapes+1);
					lastAppendIndex=j+1;
					i=j;
				}
			}else if (c == ':' ) {
				int j = i + 1;
				if ( j < statement.length && statement[j] == ':') {
					// Postgres-style "::" casting operator should be skipped
					i = i + 2;
					continue;
				}
				
				while (j < statement.length && !isParameterSeparator(statement[j])) {
					j++;
				}
				if (j - i > 1) {
					String parameter = sql.substring(i + 1, j);//参数名
					originalSql.append(sql,lastAppendIndex,i);
					actualSql.append(sql,lastAppendIndex,i);
					lastAppendIndex=i;
					int startIndex=originalSql.length();
					parameterList.add(new NamedParameterHolder(parameter, startIndex, startIndex+parameter.length()+1));
					originalSql.append(sql,lastAppendIndex,j);
					actualSql.append("?");
					lastAppendIndex=j;
				}
				
				i = j - 1;
			} else {
				//转义处理
				if (c == '\\') {
					int j = i + 1;
					if (j < statement.length && (statement[j] == '?'||statement[j] == ':'||statement[j] == '$')) {
						//跳过转义字符 并且把转义字符从原始SQL中剔除
						originalSql.append(sql,lastAppendIndex,i);
						actualSql.append(sql,lastAppendIndex,i);
						lastAppendIndex=j;
						i = i + 2;
						continue;
					}
				}
				if (c == '?') {
					int j = i + 1;
					if (j < statement.length && (statement[j] == '?' || statement[j] == '|' || statement[j] == '&')) {
						// Postgres-style "??", "?|", "?&" operator should be skipped
						i = i + 2;
						continue;
					}
					
					parameterList.add(new UnNamedParameterHolder(i,unnamedParameterCount++));
					
				}
			}
			i++;
		}
		originalSql.append(sql,lastAppendIndex,sql.length());
		actualSql.append(sql,lastAppendIndex,sql.length());
		return new VariableParsedSql(originalSql.toString(),actualSql.toString(),parameterList);
	}



	/**
	 * 跳过SQL语句中的注释和带引号的声明
	 * @param statement character array containing SQL statement
	 * @param position current position of statement
	 * @return next position to process after any comments or quotes are skipped
	 */
	private static int skipCommentsAndQuotes(char[] statement, int position) {
		for (int i = 0; i < START_SKIP.length; i++) {
			if (statement[position] == START_SKIP[i].charAt(0)) {
				boolean match = true;
				for (int j = 1; j < START_SKIP[i].length(); j++) {
					if (statement[position + j] != START_SKIP[i].charAt(j)) {
						match = false;
						break;
					}
				}
				if (match) {
					int offset = START_SKIP[i].length();
					for (int m = position + offset; m < statement.length; m++) {
						if (statement[m] == STOP_SKIP[i].charAt(0)) {
							boolean endMatch = true;
							int endPos = m;
							for (int n = 1; n < STOP_SKIP[i].length(); n++) {
								if (m + n >= statement.length) {
									// last comment not closed properly
									return statement.length;
								}
								if (statement[m + n] != STOP_SKIP[i].charAt(n)) {
									endMatch = false;
									break;
								}
								endPos = m + n;
							}
							if (endMatch) {
								// found character sequence ending comment or quote
								return endPos + 1;
							}
						}
					}
					// character sequence ending comment or quote not found
					return statement.length;
				}
			}
		}
		return position;
	}

	/**
	 * 根据paramSource拼接最终执行的SQL
	 */
	static String buildActualSql(VariableParsedSql parsedSql, @Nullable SqlParameterSource paramSource) {
		String originalSql = parsedSql.getOriginalSql();
		List<ParameterHolder> parameterHolderList=parsedSql.getParameterHolderList();
		if (parameterHolderList.isEmpty()){
			return originalSql;
		}
		StringBuilder actualSql = new StringBuilder(originalSql.length());
		int lastIndex = 0;
		for (int i = 0; i < parameterHolderList.size(); i++) {
			ParameterHolder parameterHolder=parameterHolderList.get(i);
			int startIndex = parameterHolder.getStartIndex();
			int endIndex = parameterHolder.getEndIndex();
			actualSql.append(originalSql, lastIndex, startIndex);
			actualSql.append(parameterHolder.parse(paramSource));
			lastIndex = endIndex;
		}
		actualSql.append(originalSql, lastIndex, originalSql.length());
		return actualSql.toString();
	}
	
	
	public static SqlBuild build(VariableParsedSql parsedSql, Object param) {
		SqlBuild sqlBuild=new SqlBuild();
		int variableParameterCount=parsedSql.getVariableParameterCount();
		int namedParameterCount=parsedSql.getNamedParameterCount();
		int unNamedParameterCount=parsedSql.getUnNamedParameterCount();
		SqlParameterSource paramSource=null;
		if(param!=null){
			if(namedParameterCount>0||variableParameterCount>0) {//具名参数
				if(param instanceof SqlParameterSource) {
					paramSource=(SqlParameterSource)param;
				}else if(param instanceof Map){
					paramSource=new _MapSqlParameterSource((Map<String,?>)param);
				}else{
					_MapSqlParameterSource mapSqlParameterSource=new _MapSqlParameterSource();
					if (param instanceof Collection) {
						param=((Collection<?>) param).toArray();
					}
					if (param instanceof Object[]) {
						//Object[] obj=(Object[])param;
						List<ParameterHolder> parameterHolderList=parsedSql.getParameterHolderList();
						for(int i=0;i<parameterHolderList.size();i++) {
							//TODO ...
							/*
							ParameterHolder parameterHolder=parameterHolderList.get(i);
							if(parameterHolder instanceof NamedParameterHolder) {

							}else {
								// ....
							}
							*/
						}
					}else {
						ParameterHolder parameterHolder=parsedSql.getParameterHolderList().get(0);
						if(parameterHolder instanceof NamedParameterHolder) {
							
							mapSqlParameterSource.addValue(((NamedParameterHolder)parameterHolder).getParameterName(), param);
						}
						//TODO ...
						/*
						else {

						}
						*/
					}
					//转换成SqlParameterSource
					paramSource=mapSqlParameterSource;
				}
			}else if(unNamedParameterCount>0) {//未命名参数
				
				if(param instanceof UnNamedSqlParameterSource) {
					paramSource=(UnNamedSqlParameterSource)param;
				}else if(param instanceof SqlParameterSource) {
					paramSource=new UnNamedSqlParameterSource(param);
				}else if(param instanceof Map<?,?>){
					paramSource=new UnNamedSqlParameterSource(param);
				}else {
					UnNamedSqlParameterSource unNamedSqlParameterSource=new UnNamedSqlParameterSource();
					if (param instanceof Object[]) {
						unNamedSqlParameterSource.addValue((Object[])param);
					}else if (param instanceof Collection) {
						unNamedSqlParameterSource.addValue((Collection<?>)param);
					} else {
						unNamedSqlParameterSource.addValue(param);
					}
					//转换成
					paramSource=unNamedSqlParameterSource;
				}
				
			}
		}
		Object[] params = SqlParser.buildValueArray(parsedSql, paramSource);
		sqlBuild.setParams(params);
		sqlBuild.setActualSql(parsedSql.getActualSql());//初始值
		List<ParameterHolder> parameterHolderList=parsedSql.getParameterHolderList();
		
		//可变参数=0
		if(variableParameterCount==0){
			if(params.length>parameterHolderList.size()) {
				sqlBuild.setActualSql(SqlParser.buildActualSql(parsedSql, paramSource));
			}
		}else {
			sqlBuild.setActualSql(SqlParser.buildActualSql(parsedSql, paramSource));
		}
		return sqlBuild;
	}
	
	
	static Object[] buildValueArray(VariableParsedSql parsedSql, SqlParameterSource paramSource) {
		
		return buildValueList( parsedSql,  paramSource).toArray();
	}
	
	static List<Object> buildValueList(VariableParsedSql parsedSql, SqlParameterSource paramSource) {
		List<ParameterHolder> parameterHolderList=parsedSql.getParameterHolderList();
		List<Object> valueList =new ArrayList<Object>(parameterHolderList.size());
		for (int i = 0; i < parameterHolderList.size(); i++) {
			ParameterHolder parameterHolder=parameterHolderList.get(i);
			parameterHolder.appendValueList(valueList, paramSource);
		}
		return valueList ;
	}
	
	static void appendParameters(StringBuilder actualSql,Object value) {
		if (value instanceof SqlParameterValue) {
			value = ((SqlParameterValue) value).getValue();
		}
		if (value instanceof Object[]) {
			 value=Arrays.asList((Object[])value);
		}
		if (value instanceof Iterable) {
			Iterator<?> entryIter = ((Iterable<?>) value).iterator();
			while (entryIter.hasNext()) {
				appendParameters( actualSql,entryIter.next());
			}
		} else {
			if (actualSql.length()>0&&actualSql.charAt(actualSql.length()-1)=='?') {
				actualSql.append(",");
			}
			actualSql.append('?');
		}
	}
	
	

	static void appendValueArray(List<Object> paramList,Object paramValue) {
		if (paramValue instanceof Object[]) {
			paramValue=Arrays.asList((Object[])paramValue);
		}
		if (paramValue instanceof Iterable) {
			Iterator<?> entryIter = ((Iterable<?>) paramValue).iterator();
			while (entryIter.hasNext()){
				appendValueArray(paramList,entryIter.next());
			}
		} else {
			paramList.add(paramValue);
		}
	}
	
	

	/**
	 * 确定参数名称是否在当前位置结束，即给定字符是否可以用作分隔符
	 */
	private static boolean isParameterSeparator(char c) {
		return (c < 128 && separatorIndex[c]) || Character.isWhitespace(c);
	}
	

}
